'/
' GUI Library
' Written by Anton Berlin, 2010.
' 
' =========================================================================
' 
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU Lesser General Public License as published
' by the Free Software Foundation; either version 3 of the License.
' 
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.
' 
' You should have received a copy of the GNU Lesser General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'/

superstrict

import "GUI_Gadget.bmx"
import "GUI_MenuItem.bmx"
import "GUI_Event.bmx"
import "GUI_Def.bmx"

import "../ArrayList/ArrayList.bmx"

type GUI_Menu extends GUI_Gadget

	'// -------------------------------------------------------------------------------------------------------------
	'// Button states.
	'// -------------------------------------------------------------------------------------------------------------
	global STATE_IDLE:byte = 0;
	global STATE_PRESSED:byte = 1;
	global STATE_OPEN:byte = 2;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Returns true if the mouse is hovering over any of the items in this menu.
	'// -------------------------------------------------------------------------------------------------------------
	method mouseHoveringOverItems:byte()
	
		local totalMenuItems:int = m_menuItems.count();
		local itemHeight:int = textHeight("|") * 1.2;
		local width:int = textWidth(m_longestMenuItem.m_text) * 1.5;
		local height:int = totalMenuItems * itemHeight + 5;
		local startY:int = m_posY + m_height + 3;
		local my:int = mouseY();
		local itemStartY:int;
		
		if (mouseX() < m_posX or mouseX() > m_posX + width ..
		or  mouseY() < startY or mouseY() > startY + height) ..
			return false;
		
		'// Select the menu item the mouse is hovering over.
		local index:int = -1;
		for local menuItem:GUI_MenuItem = eachin m_menuItems.m_content
		
			index :+ 1;
			
			itemStartY = startY + 2 + index * itemHeight;
			
			if (my >= itemStartY and my <= itemStartY + itemHeight) then
			
				m_selectedMenuItem = menuItem;
				exit;
			
			end if
		
		next
		
		return true;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Update this gadget.
	'// -------------------------------------------------------------------------------------------------------------
	method update()
	
		if (not mouseHoveringOverItems()) ..
			m_selectedMenuItem = null;
		
		if (not mouseHovering() or not mouseDown(1)) ..
			m_state = STATE_IDLE;
		
		if (m_state = STATE_PRESSED) then
		
			toggle();
			m_state = STATE_OPEN;
		
		end if
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Toggle the menu open state to on or off.
	'// -------------------------------------------------------------------------------------------------------------
	method toggle()
	
		if (m_open) ..
			m_open = false; ..
		else ..
			m_open = true;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Draw this gadget.
	'// -------------------------------------------------------------------------------------------------------------
	method draw()
	
		if (not m_visible) ..
			return;
		
		'// Setup the default drawing settings.
		drawutil.setColorInit(m_primitiveR, m_primitiveG, m_primitiveB);
		drawutil.setAlphaInit(m_transparency);
		drawutil.setFontInit(null);
		
		'// Draw the design of the gadget.
		design();
		
		'// Reset drawing settings.
		drawutil.resetColor();
		drawutil.resetAlpha();
		drawutil.resetFont();
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Draw the design of this gadget.
	'// -------------------------------------------------------------------------------------------------------------
	method design()
	
		drawMenuObject();
		drawMenuItems();
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Draw the menu items.
	'// -------------------------------------------------------------------------------------------------------------
	method drawMenuItems()
	
		if (not m_open) ..
			return;
		
		'// Prepare colors.
		local color1R:int;
		local color1G:int;
		local color1B:int;
		
		local color2R:int;
		local color2G:int;
		local color2B:int;
		
		color1R = m_primitiveR * 1.5;
		color1G = m_primitiveG * 1.5;
		color1B = m_primitiveB * 1.5;
		
		color2R = m_primitiveR * 0.5;
		color2G = m_primitiveG * 0.5;
		color2B = m_primitiveB * 0.5;
		
		'// Calculate width and height of the menu background.
		local totalMenuItems:int = m_menuItems.count();
		local itemHeight:int = textHeight("|") * 1.2;
		local width:int = textWidth(m_longestMenuItem.m_text) * 1.5;
		local height:int = totalMenuItems * itemHeight + 5;
		if (height < textHeight("|")) ..
			height = textHeight("|");
		local startY:int = m_posY + m_height + 3;
		local itemStartX:int = width / 6;
		
		'// Draw the primitive of the menu background.
		setColor(m_primitiveR, m_primitiveG, m_primitiveB);
		drawRect(m_posX, startY, width, height);
		
		'// Draw the borders of the menu background.
		setColor(color1R, color1G, color1B);
		drawLine(m_posX, startY, m_posX + width, startY, false);
		drawLine(m_posX, startY, m_posX, startY + height, false);
		setColor(color2R, color2G, color2B);
		drawLine(m_posX, startY + height, m_posX + width, startY + height, false);
		drawLine(m_posX + width, startY, m_posX + width, startY + height, false);
		
		'// Draw the menu items.
		local index:int = -1;
		for local menuItem:GUI_MenuItem = eachin m_menuItems.m_content
		
			index :+ 1;
			
			if (menuItem.m_bar) then
			
				local lineY:int = startY + 2 + index * itemHeight + itemHeight / 2;
				setColor(color2R, color2G, color2B);
				drawLine(m_posX + 2, lineY, m_posX + width - 3, lineY);
				setColor(color1R, color1G, color1B);
				drawLine(m_posX + 2, lineY + 1, m_posX + width - 3, lineY + 1);
			
			else
			
				if (menuItem = m_selectedMenuItem) then
				
					setColor(m_selectionR, m_selectionG, m_selectionB);
					drawRect(m_posX + 2, startY + 2 + index * itemHeight, width - 4, itemHeight + 2);
					setColor(255 - m_textColorR, 255 - m_textColorG, 255 - m_textColorB);
				
				else
				
					setColor(m_textColorR, m_textColorG, m_textColorB);
				
				end if
				
				if (menuItem.m_active) then
				
					drawText(menuItem.m_text, itemStartX, startY + 3 + index * itemHeight);
				
				else
				
					if (menuItem = m_selectedMenuItem) then
					
						setColor(max(192 - m_textColorR, 0), max(192 - m_textColorG, 0), max(192 - m_textColorB, 0)); ..
						drawText(menuItem.m_text, itemStartX, startY + 3 + index * itemHeight);
					
					else
					
						setColor(m_primitiveR * 2, m_primitiveG * 2, m_primitiveB * 2);
						drawText(menuItem.m_text, 1 + itemStartX, 1 + startY + 3 + index * itemHeight);
						setColor(255 - m_primitiveR, 255 - m_primitiveR, 255 - m_primitiveR);
						drawText(menuItem.m_text, itemStartX, startY + 3 + index * itemHeight);
					
					end if
				
				end if
			
			end if
		
		next
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Draw the menu object (the button-like menu-box-thingy).
	'// -------------------------------------------------------------------------------------------------------------
	method drawMenuObject()
	
		local mouse_hovering:byte = mouseHovering();
		
		'// Prepare colors.
		local color1R:int;
		local color1G:int;
		local color1B:int;
		
		local color2R:int;
		local color2G:int;
		local color2B:int;
		
		local oldPrimitiveR:short = m_primitiveR;
		local oldPrimitiveG:short = m_primitiveG;
		local oldPrimitiveB:short = m_primitiveB;
		
		local oldTextR:short = m_textColorR;
		local oldTextG:short = m_textColorG;
		local oldTextB:short = m_textColorB;
		
		if (mouse_hovering or m_open) then
		
			if (not m_active) then
			
				m_primitiveR :* 0.75;
				m_primitiveG :* 0.75;
				m_primitiveB :* 0.75;
			
			end if
			
			if (m_open) then
			
				color1R = m_primitiveR * 0.5;
				color1G = m_primitiveG * 0.5;
				color1B = m_primitiveB * 0.5;
				
				color2R = m_primitiveR * 1.5;
				color2G = m_primitiveG * 1.5;
				color2B = m_primitiveB * 1.5;
			
			else
			
				color1R = m_primitiveR * 1.5;
				color1G = m_primitiveG * 1.5;
				color1B = m_primitiveB * 1.5;
				
				color2R = m_primitiveR * 0.5;
				color2G = m_primitiveG * 0.5;
				color2B = m_primitiveB * 0.5;
			
			end if
		
		end if
		
		'// Draw the primitive of the gadget.
		setColor(m_primitiveR, m_primitiveG, m_primitiveB);
		drawRect(m_posX, m_posY, m_width, m_height);
		
		'// Border.
		if (mouse_hovering or m_open) then
		
			setColor(color1R, color1G, color1B);
			drawLine(m_posX, m_posY, m_posX + m_width, m_posY, false);
			drawLine(m_posX, m_posY, m_posX, m_posY + m_height, false);
			setColor(color2R, color2G, color2B);
			drawLine(m_posX, m_posY + m_height, m_posX + m_width, m_posY + m_height, false);
			drawLine(m_posX + m_width, m_posY, m_posX + m_width, m_posY + m_height, false);
		
		end if
		
		'// ------------ Button Text -----------
		
		setImageFont(m_font);
		
		local x:int = m_posX + m_width / 2 - textWidth(m_text) / 2;
		local y:int = m_posY + m_height / 2 - textHeight(m_text) / 2 - 1;
		
		local offset:short = 0;
		
		if (m_open) ..
			offset = 1;
		
		'// Shadow.
		if (m_useShadows) then
		
			setAlpha(m_transparency * 0.5);
			setColor(0, 0, 0);
			drawText(m_text, x + offset + 3, y + offset + 3);
		
		end if
		
		if (m_useBorders) then
		
			'// Upper border.
			setAlpha(m_transparency * 1.0);
			setColor(m_textColorR * 0.1, m_textColorG * 0.1, m_textColorB * 0.1)
			drawText(m_text, x + offset, y + offset);
			setAlpha(m_transparency * 1.0);
			setColor(m_textColorR * 0.1, m_textColorG * 0.1, m_textColorB * 0.1)
			drawText(m_text, x + offset, y + offset + 1);
			setColor(m_textColorR * 0.1, m_textColorG * 0.1, m_textColorB * 0.1)
			drawText(m_text, x + offset + 1, y + offset);
			
			'// Lower border.
			setColor(m_textColorR * 0.1, m_textColorG * 0.1, m_textColorB * 0.1)
			drawText(m_text, x + offset + 2, y + offset + 2);
		
		end if
		
		'// Main text.
		setColor(m_textColorR, m_textColorG, m_textColorB);
		drawText(m_text, x + offset + 1, y + offset + 1);
		
		'// Reset colors.
		if (not m_active) then
		
			m_primitiveR = oldPrimitiveR;
			m_primitiveG = oldPrimitiveG;
			m_primitiveB = oldPrimitiveB;
			
			m_textColorR = oldTextR;
			m_textcolorG = oldTextG;
			m_textcolorB = oldTextB;
		
		end if
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Execute the action of the menu item that the mouse currently hovers above.
	'// -------------------------------------------------------------------------------------------------------------
	method executeItem()
	
		if (m_selectedMenuItem and m_selectedMenuItem.m_active and not m_selectedMenuItem.m_bar) ..
			m_event = GUI_Event.create(GUI_Event.EVENT_INVOKE_METHOD, m_selectedMenuItem.m_methodHook, m_hookedObject);
		
		if (not m_selectedMenuItem or m_selectedMenuItem.m_active and not m_selectedMenuItem.m_bar) then
		
			m_open = false;
			m_state = STATE_IDLE;
		
		end if
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set the button text.
	'// -------------------------------------------------------------------------------------------------------------
	method setText(str:string)
	
		m_text = str;
		m_width = textWidth(m_text) + textWidth("....");
		m_height = textHeight(m_text) * 1.3;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set the color of the button text.
	'// -------------------------------------------------------------------------------------------------------------
	method setTextColor(r:byte, g:byte, b:byte)
	
		m_textColorR = r;
		m_textColorG = g;
		m_textColorB = b;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set the color of the menu item selection box.
	'// -------------------------------------------------------------------------------------------------------------
	method setSelectionColor(r:byte, g:byte, b:byte)
	
		m_selectionR = r;
		m_selectionG = g;
		m_selectionB = b;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set whether or not to use shadows on the button text.
	'// -------------------------------------------------------------------------------------------------------------
	method setTextShadowUsage(value:byte)
	
		m_useShadows = value;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set whether or not to use borders on the button text.
	'// -------------------------------------------------------------------------------------------------------------
	method setTextBorderUsage(value:byte)
	
		m_useBorders = value;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Set the font of the button text.
	'// -------------------------------------------------------------------------------------------------------------
	method setFont(font:TImageFont)
	
		m_font = font;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Add a menu item to this menu.
	'// -------------------------------------------------------------------------------------------------------------
	method newMenuItem(name:string, text:string, methodHook:string)
	
		if (name = "-") then
		
			local menuItem:GUI_MenuItem = new GUI_MenuItem;
			
			menuItem.m_bar = true;
			
			m_menuItems.addLast(menuItem);
			m_menuItemsMap.insert(name, menuItem);
			
			return;
		
		end if
		
		if (not name or not text or not methodHook) ..
			return;
		
		local menuItem:GUI_MenuItem = new GUI_MenuItem;
		
		m_menuItems.addLast(menuItem);
		m_menuItemsMap.insert(name, menuItem);
		
		menuItem.m_text = text;
		menuItem.m_methodHook = methodHook;
		
		if (not m_longestMenuItem or m_longestMenuItem.m_text.length < text.length) ..
			m_longestMenuItem = menuItem;
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Add a menu item to this menu.
	'// -------------------------------------------------------------------------------------------------------------
	method getMenuItem:GUI_MenuItem(name:string)
	
		if (not name or not m_menuItemsMap or m_menuItemsMap.isEmpty()) ..
			return null;
		
		return GUI_MenuItem(m_menuItemsMap.valueForKey(name));
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Press the button.
	'// -------------------------------------------------------------------------------------------------------------
	method press()
	
		if (mouseDown(1) and m_state <> STATE_OPEN) then
		
			if (mouseHovering()) ..
				m_state = STATE_PRESSED; ..
			else if (mouseHoveringOverItems()) ..
				executeItem();
		
		end if
	
	end method
	
	'// -------------------------------------------------------------------------------------------------------------
	'// The menu text to use.
	'// -------------------------------------------------------------------------------------------------------------
	field m_text:string;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Flag that determines whether or not to use shadows on the menu text.
	'// -------------------------------------------------------------------------------------------------------------
	field m_useShadows:byte = false;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Flag that determines whether or not to use borders on the menu text.
	'// -------------------------------------------------------------------------------------------------------------
	field m_useBorders:byte = false;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// The font to use on the menu text.
	'// -------------------------------------------------------------------------------------------------------------
	field m_font:TImageFont;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Flag that determines whether or not the menu is open.
	'// -------------------------------------------------------------------------------------------------------------
	field m_open:byte = false;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Containers of all the menu items that are contained inside of this menu.
	'// -------------------------------------------------------------------------------------------------------------
	field m_menuItems:ArrayList = new ArrayList;
	field m_menuItemsMap:TMap = new TMap;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Pointer to the longest menu item added to this menu.
	'// -------------------------------------------------------------------------------------------------------------
	field m_longestMenuItem:GUI_MenuItem;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Pointer to the selected menu item of this menu.
	'// -------------------------------------------------------------------------------------------------------------
	field m_selectedMenuItem:GUI_MenuItem;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Menu text color.
	'// -------------------------------------------------------------------------------------------------------------
	field m_textColorR:byte;
	field m_textColorG:byte;
	field m_textColorB:byte;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Color of the selection box.
	'// -------------------------------------------------------------------------------------------------------------
	field m_selectionR:byte;
	field m_selectionG:byte;
	field m_selectionB:byte;
	
	'// -------------------------------------------------------------------------------------------------------------
	'// Current mouse state
	'// -------------------------------------------------------------------------------------------------------------
	field m_state:byte = STATE_IDLE;

end type

